package cn.xd.itstack.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.xd.itstack.springframework.beans.BeansException;
import cn.xd.itstack.springframework.beans.factory.*;
import cn.xd.itstack.springframework.beans.factory.config.*;
import cn.xd.itstack.springframework.beans.PropertyValue;
import cn.xd.itstack.springframework.beans.PropertyValues;
import com.sun.tools.javac.util.StringUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @Classname AbstractAutoWireCapableBeanFactory
 * @Description TODO
 * @Date 2022/5/10 下午4:09
 * @Created by lvxiaodong
 */
public abstract class AbstractAutoWireCapableBeanFactory extends AbstractBeanFactory implements AutoWireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();


    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition) {
        Object bean;
        try {
            bean = beanDefinition.getBeanClass().newInstance();
            //注入属性和依赖
            applyPropertyValues(beanName, bean, beanDefinition);
            //执行Bean的初始化方法和BeanPostProcessor的前置后后置方法
            initializeBean(beanName, bean, beanDefinition);
        } catch (InstantiationException | IllegalAccessException e) {
            throw new BeansException("Instantiation of bean failed", e);
        }
        //注册实现了Disposable接口的Bean
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
        if (beanDefinition.isSingleton()) {
            addSingleton(beanName, bean);
        }
        return bean;
    }


    /**
     * 注册实现了Disposable接口的Bean
     *
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void registerDisposableBeanIfNecessary(String beanName,
                                                     Object bean,
                                                     BeanDefinition beanDefinition) {
        //非singleton 不执行销毁方法
        if (!beanDefinition.isSingleton()) {
            return;
        }
        if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
            registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
        }
    }


    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object... args) {
        Object bean;
        try {
            //判断是否返回代理对象
            bean = resolveBeforeInstantiation(beanDefinition,beanName);
            if(bean !=null){
                return bean;
            }
            //实例化
            bean = createBeanInstance(beanName, beanDefinition, args);
            //设置属性之前，允许beanPostProcessor修改属性值
            applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName,bean,beanDefinition);
            //注入属性和依赖
            applyPropertyValues(beanName, bean, beanDefinition);
            //之子那个Bean的初始化方法和BeanPostProcessor的前置和后置方法
            initializeBean(beanName, bean, beanDefinition);

        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
        if (beanDefinition.isSingleton()) {
            addSingleton(beanName, bean);
        }
        return bean;
    }

    /**
     * 设置属性之前允许BeanPostProcessor 修改属性
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition){

        for (BeanPostProcessor beanPostProcess :  getBeanPostProcessors()) {
            if(beanPostProcess instanceof InstantiationAwareBeanPostProcessor){
                PropertyValues propertyValues = ((InstantiationAwareBeanPostProcessor) beanPostProcess).postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName);
                if(propertyValues !=null){
                    for (PropertyValue propertyValue : propertyValues.getPropertyValues()){
                        beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
                    }
                }
            }
        }

    }

    protected  Object resolveBeforeInstantiation(BeanDefinition beanDefinition, String beanName){
        Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName);
        if(bean!=null){
            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        }
        return bean;
    }

    protected  Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName){
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()){
            if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
                Object result = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessBeforeInstantiation(beanClass, beanName);
                if (null != result) return result;
            }
        }
        return null;
    }

    /**
     * 注入属性和依赖
     *
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void applyPropertyValues(String beanName,
                                       Object bean,
                                       BeanDefinition beanDefinition) {
        try {
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue pv : propertyValues.getPropertyValues()) {
                String name = pv.getName();
                Object value = pv.getValue();
                if (value instanceof BeanReference) {
                    //引用对象
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                BeanUtil.setFieldValue(bean, name, value);
            }
        } catch (Exception e) {
            throw new BeansException("Error setting property values" + beanName);
        }

    }

    /**
     * 根据 BeanDefinition， beanName ，构造参数实例化对象
     *
     * @param beanName
     * @param beanDefinition
     * @param args
     * @return
     */
    protected Object createBeanInstance(String beanName,
                                        BeanDefinition beanDefinition,
                                        Object... args) {
        Constructor constructor = null;
        Constructor[] declaredConstructor = beanDefinition.getBeanClass().getDeclaredConstructors();
        for (Constructor ctr : declaredConstructor) {
            if (args != null && args.length == ctr.getParameterCount()) {
                constructor = ctr;
                break;
            }

        }
        return instantiationStrategy.instantiate(beanDefinition, beanName, constructor, args);
    }

    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }


    /**
     * 执行Bean的初始化方法和BeanPostProcessor的前置和后置方法
     *
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    private Object initializeBean(String beanName,
                                  Object bean,
                                  BeanDefinition beanDefinition) {
        // invoke aware Methods
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }
        if (bean instanceof BeanClassLoaderAware) {
            ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
        }
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        //1、执行BeanPostProcessor的前置处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        //2、执行初始化方法
        try {
            invokeInitMethods(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
        }
        //3、执行BeanPostProcessor的后置处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        return wrappedBean;
    }

    /**
     * 执行初始化方法
     *
     * @param beanName
     * @param wrappedBean
     * @param beanDefinition
     */
    private void invokeInitMethods(String beanName,
                                   Object wrappedBean,
                                   BeanDefinition beanDefinition) throws Exception {
        //1、判断Bean类型
        if (wrappedBean instanceof InitializingBean) {
            ((InitializingBean) wrappedBean).afterPropertiesSet();
        }
        //2、BeanDefinition的初始化方法明是否为空
        String initMethodName = beanDefinition.getInitMethodName();
        if (StrUtil.isNotBlank(initMethodName)) {
            //3、根据反射调用初始化方法
            Method method = beanDefinition.getBeanClass().getMethod(initMethodName);
            if (method == null) {
                throw new BeansException("Could not find an init method named '" +
                        initMethodName + "' on bean with name '" + beanName + "'");
            }
            method.invoke(wrappedBean);
        }

    }

}
